package org.jeecg.common.aspect;

import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.aspect.annotation.Sign;
import org.jeecg.common.constant.enums.EncryptEnum;
import org.jeecg.common.exception.JeecgCloudException;
import org.jeecg.common.util.Md5Util;
import org.jeecg.common.util.ObjectUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;


/**
 * @author tomliu
 * @description: TODO
 * @date 2022/11/2612:41
 */
@Aspect
@Component
@Slf4j
public class SignatureAspect {

    @Pointcut("@annotation(org.jeecg.common.aspect.annotation.Sign)")
    public void pointCut() {

    }

    @Around("pointCut()")
    public Object arround(ProceedingJoinPoint point) throws  Throwable{
        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Sign pd = method.getAnnotation(Sign.class);
        EncryptEnum type = pd.type();
        String[]  excludes = pd.exclude();
        Object[] args = point.getArgs();

        SortedMap<String,String> map = logParam(args, List.of(excludes));
        if (map != null) {
            String sign = map.get("sign").toUpperCase();
            map.remove("sign");
            String realSign = createSign("utf8", map);
            if (!realSign.equals(sign)) {
                throw new JeecgCloudException("签名校验错误");
            }
        }

        return  point.proceed();
    }

    public static String createSign(String characterEncoding, SortedMap<String, String> parameters) {
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String,String> entry : parameters.entrySet()) {
            if (StringUtils.isNotBlank(entry.getValue())) {
                sb.append(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        String s = sb.toString();
        if (s.length() > 0) {
            s = s.substring(0, sb.toString().length() - 1);
        }
        log.info("待加密字符串:" + s);
       return Md5Util.md5Encode(s,characterEncoding).toUpperCase();
    }


    /**
     * 使用javassist来获取方法参数名称
     *
     * @param class_name  类名
     * @param method_name 方法名
     * @return
     * @throws Exception
     */
    private String[] getFieldsName(String class_name, String method_name) throws Exception {
        Class clazz = Class.forName(class_name);
        String clazz_name = clazz.getName();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(clazz);
        pool.insertClassPath(classPath);
        CtClass ctClass = pool.get(clazz_name);
        CtMethod ctMethod = ctClass.getDeclaredMethod(method_name);
        MethodInfo methodInfo = ctMethod.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            return null;
        }
        String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];
        int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramsArgsName.length; i++) {
            paramsArgsName[i] = attr.variableName(i + pos);
        }
        return paramsArgsName;
    }

    /**
     * 打印方法参数值  基本类型直接打印，非基本类型需要重写toString方法
     * @param paramsArgsValue 方法参数值数组
     */
    private SortedMap<String, String> logParam( Object[] paramsArgsValue, List<String> excludes ) {
        Map<String, String> map = new HashMap<>();
        if (ArrayUtils.isEmpty(paramsArgsValue)) {
            log.info("该方法没有参数");
            return null;
        }
//        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < paramsArgsValue.length; i++) {
            //参数值
            Object obj = paramsArgsValue[i];
            try {
                map = ObjectUtils.getFieldList(obj);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        for(String str : excludes){
            map.remove(str);
        }

        return new TreeMap<>(map);
    }
    /**
     * 判断是否为基本类型：包括String
     *
     * @param clazz clazz
     * @return true：是;     false：不是
     */
    private boolean isPrimite(Class clazz) {
        if (clazz.isPrimitive() || clazz == String.class) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 过滤不需要属性
     * @param field
     * @return
     */
    private static Boolean needFilterField(Field field){
        // 过滤静态属性
        if(Modifier.isStatic(field.getModifiers())){
            return true;
        }
        // 过滤transient 关键字修饰的属性
        if(Modifier.isTransient(field.getModifiers())){
            return true;
        }
        return false;

    }

}
